import * as THREE from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass'

export default class Bloom {
  static BLOOM_SCENE = 1

  constructor(_viewer) {
    this.bloomLayer = new THREE.Layers()
    this.bloomLayer.set(Bloom.BLOOM_SCENE)
    this.darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' })
    this.materials = {}
    this.renderPass = new RenderPass(_viewer.scene, _viewer.camera)

    // bloomComposer
    this.bloomComposer = this.getBloomComposer(_viewer)
    // finalComposer
    this.finalComposer = this.getFinalComposer(_viewer)

    _viewer.bloomComposer = this.bloomComposer
    _viewer.finalComposer = this.finalComposer
  }

  getBloomComposer(viewer) {
    const bloomComposer = new EffectComposer(viewer.renderer)
    const unBloomPass = new UnrealBloomPass(
      new THREE.Vector2(window.innerWidth, window.innerHeight), // 泛光所覆盖的场景大小，是Vector2类型的向量
      0.15, // 泛光的强度，值越大明亮的区域越亮，较暗区域变亮的范围越广
      10, // 泛光散发的半径
      0 // 产生泛光的光照强度阈值，如果照在物体上的光照强度大于该值就会产生泛光
    )
    bloomComposer.renderToScreen = false

    bloomComposer.addPass(this.renderPass)
    bloomComposer.addPass(unBloomPass)
    return bloomComposer
  }

  getFinalComposer(viewer) {
    const finalComposer = new EffectComposer(viewer.renderer)
    const finalPass = new ShaderPass(
      new THREE.ShaderMaterial({
        uniforms: {
          baseTexture: { value: null },
          bloomTexture: { value: this.bloomComposer.renderTarget2.texture }
        },
        vertexShader: `
          varying vec2 vUv;
  
          void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
          }
        `,
        fragmentShader: `
          uniform sampler2D baseTexture;
          uniform sampler2D bloomTexture;
  
          varying vec2 vUv;
  
          void main() {
            gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0, 1.0, 1.0, 0.1 ) * texture2D( bloomTexture, vUv ) );
          }
        `,
        defines: {}
      }), 'baseTexture'
    )
    finalPass.needsSwap = true
    finalComposer.addPass(this.renderPass)
    finalComposer.addPass(finalPass)
    return finalComposer
  }

  darkenNonBloomed(obj) {
    if (obj.isMesh && this.bloomLayer.test(obj.layers) === false) {
      this.materials[obj.uuid] = obj.material
      obj.material = this.darkMaterial
    }
  }

  restoreMaterial(obj) {
    if (this.materials[obj.uuid]) {
      obj.material = this.materials[obj.uuid]
      delete this.materials[obj.uuid]
    }
  }

  update(viewer) {
    viewer.scene.traverse(this.darkenNonBloomed.bind(this))
    this.bloomComposer.render()
    viewer.scene.traverse(this.restoreMaterial.bind(this))
    this.finalComposer.render()
  }
}
